#include <drivers.h>
#include <drivers/pin.h>

static struct pin _pin;

static size_t pin_read(struct device *dev, const size_t offset,
			void *buffer, size_t size)
{
	struct device_pin_status *status = (struct device_pin_status *)buffer;
	struct pin *pin = container_of(dev, struct pin, parent);

	if (status == NULL || size != sizeof(*status))
		return 0;

	status->status = pin->ops->read(status->pin);

	return size;
}

static size_t pin_write(struct device *dev, const size_t offset, const void *buffer, size_t size)
{
	int32_t ret = 0;
	struct device_pin_status *status = (struct device_pin_status *)buffer;
	struct pin *pin = container_of(dev, struct pin, parent);

	ret = pin->ops->write(status->pin, status->status);
	if (ret != 0)
	  return 0;

	return size;
}
static err_t pin_control(struct device *dev, int cmd, void *args)
{
	err_t ret = 0;
	struct pin *pin = container_of(dev, struct pin, parent);
	base_t pin_num = *(base_t*)args;

	if (cmd > PIN_MODE_UNKOWN)
	  return -EINVAL;

	ret = pin->ops->mode(pin_num, (enum pin_mode)cmd);

	return ret;
}

const static struct device_ops pin_ops = {
	.init = NULL,
	.close = NULL,
	.open = NULL,
	.read = pin_read,
	.write = pin_write,
	.control = pin_control
};

err_t hw_pin_register(const char *name, struct pin_ops *ops,
			void *private_data)
{
	err_t ret = 0;

	_pin.ops = ops;
	_pin.parent.type = DEVICE_TYPE_CHAR;
	_pin.parent.ops = &pin_ops;
	_pin.parent.private_data = private_data;

	device_register(&_pin.parent, name, DEVICE_FLAG_RW);

	return ret;
}

base_t hw_pin_get(const char *name)
{
	assert(_pin.ops != NULL);

	if (_pin.ops->pin_get == NULL)
		return -EIO;

	return _pin.ops->pin_get(name);
}

base_t hw_pin_read(const base_t pin_num)
{
	assert(_pin.ops != NULL);

	return _pin.ops->read(pin_num);
}

err_t hw_pin_write(const base_t pin_num,
			const base_t value)
{
	assert(_pin.ops != NULL);

	return _pin.ops->write(pin_num, value);
}

err_t hw_pin_mode(const base_t pin_num,
			const enum pin_mode mode)
{
	assert(_pin.ops != NULL);

	if (_pin.ops->mode == NULL)
		return -EIO;

	return _pin.ops->mode(pin_num, mode);
}

err_t hw_pin_attach_irq(const base_t pin_num,
			const enum pin_irq_mode mode,
			void (*handler)(void *args),
			void *args,
            uint16_t priority)
{
	assert(_pin.ops != NULL);

	if (_pin.ops->attach_irq == NULL)
		return -EIO;

	return _pin.ops->attach_irq(pin_num, mode, handler, args, priority);
}

err_t hw_pin_detach_irq(const base_t pin_num)
{
	assert(_pin.ops != NULL);

	if (_pin.ops->detach_irq == NULL)
		return -EIO;

	return _pin.ops->detach_irq(pin_num);
}

err_t hw_pin_irq_enable(const base_t pin_num,
			const uint8_t enable)
{
	assert(_pin.ops != NULL);

	if (_pin.ops->enable_irq == NULL)
		return -EIO;

	return _pin.ops->enable_irq(pin_num, enable);
}
